/*
   Copyright 2011 Brook Smith

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package org.xempot.xform;

import java.util.Set;

/**
 * Basic parser
 * <p/>
 * ${xpath [; template[; template]]} ${xpath [; command()]} ${command()}
 * <p/>
 * <b>Break down</b> ${xpath} - output the value of the xpath ${xpath;template}
 * - iterate through the result set and call the template for each hit
 * ${xpath;template;template} -as above except if the result set is empty then
 * call the other template equivalent to if, else ${command()} - execute the
 * pre-defined command ${template} - call the template ${xpath;command()} -
 * execute the pre-defined command, with the node(s) as a parameter - envisaged
 * as a way of providing format specifiers
 * <p/>
 * commands have () appended (to help differentiate)
 * 
 * @author brook
 * 
 */
public class InstructionParser {

	/**
	 * Determine if the command corrospands to one of the pre-defined commands
	 * 
	 * @param string
	 * @return
	 */
	private static boolean isCommand(String string, Set<String> commands) {
		return commands.contains(string);
	}

	/**
	 * validates against the list of templates available
	 * 
	 * @param string
	 * @return
	 */
	private static boolean isTemplate(String string, Set<String> templates) {
		return templates.contains(string);
	}

	public static void main(String[] args) {
		System.out.println(stripCommandWrapper("${/*;dothis;dothat}"));
		System.out.println(stripCommandWrapper("/*;dothis;dothat"));

		// TODO add guice
		try {
			System.out.println(parseCommand("${/*;command()}", null));
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			System.out.println(parseCommand("${/*;template;alttemplate}", null));
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			System.out.println(parseCommand("${/*;template}", null));
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			System.out.println(parseCommand("${command()}", null));
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			System.out.println(parseCommand("${template}", null));
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			System.out.println(parseCommand("${/*}", null));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static InstructionData parseCommand(String command, Transform transform) throws InstructionException {
		Set<String> templates = transform.getTemplateNames();
		Set<String> commands = transform.getCommands();

		String[] tokens = stripCommandWrapper(command).split(";");
		if (tokens.length == 0) {
			throw new InstructionException("well that ain't gunner work no instruction, length=0");
		} else if (tokens.length == 1) {
			// xpath of command
			if (isCommand(tokens[0], commands)) {
				return new InstructionData(Instruction.command, null, tokens[0].trim(), null, null);
			} else if (isTemplate(tokens[0], templates)) {
				return new InstructionData(Instruction.template, null, null, tokens[0].trim(), null);
			} else {
				return new InstructionData(Instruction.xpath, tokens[0].trim(), null, null, null);
			}
		} else if (tokens.length == 2) {
			// xpath of command
			if (isCommand(tokens[1], commands)) {
				// xpath;comand
				return new InstructionData(Instruction.xpathcommand, tokens[0].trim(), tokens[1].trim(), null, null);
			} else {
				// xpath;template
				return new InstructionData(Instruction.xpathtemplate, tokens[0].trim(), null, tokens[1].trim(), null);
			}
		} else if (tokens.length == 3) {
			// xpath; template; template
			return new InstructionData(Instruction.xpathif, tokens[0].trim(), null, tokens[1].trim(), tokens[2].trim());
		} else {
			throw new InstructionException("Unknown command type " + command);
		}

	}

	private static String stripCommandWrapper(String command) {
		return command.replaceAll("^\\$\\{(.+)\\}$", "$1");
	}

}
